`timescale 1ns / 1ps

`include "macro.vh"

module my_cu(
	input clk,
	input rst,

	input [31:0]inst,

	output reg [1:0]pc_oper,
	output reg [31:0]pc_jmp,
	output reg [31:0]pc_br,

	output reg [3:0]alu_op,
	output reg [31:0]alu_num1,
	output reg [31:0]alu_num2,
	input [31:0]alu_res,

	output reg ram_wen,
	output reg [31:0]ram_addr,
	output reg [31:0]ram_wdata,
	input [31:0]ram_rdata,

	output reg reg_wen,
	output reg [31:0]reg_waddr,
	output reg [31:0]reg_wdata,
	output reg [31:0]reg_raddr1,
	output reg [31:0]reg_raddr2,
	input [31:0] reg_rdata1,
	input [31:0] reg_rdata2
    );

	wire [5:0]inst_op;
	wire [4:0]inst_num1;
	wire [4:0]inst_num2;
	wire [4:0]inst_num3;
	wire [15:0]inst_imme;
	wire [4:0]inst_shamt;
	wire [5:0]inst_func;
	wire [25:0]inst_index;

	assign inst_op = inst[31:26];
	assign inst_num1 = inst[25:21];
	assign inst_num2 = inst[20:16];
	assign inst_num3 = inst[15:11];
	assign inst_imme = inst[15:0];
	assign inst_shamt = inst[10:6];
	assign inst_func = inst[5:0];
	assign inst_index = inst[25:0];

	always @(*) begin
		if (!rst) begin
			pc_oper <= `PC_STOP;
			pc_jmp <= 32'h00000000;
			pc_br <= 32'h00000000;
			alu_op <= `PC_NORMAL;
			alu_num1 <= 32'h00000000;
			alu_num2 <= 32'h00000000;
			ram_wen <= 1'b0;
			ram_addr <= 32'h00000000;
			ram_wdata <= 32'h00000000;
			reg_wen <= 1'b0;
			reg_waddr <= 32'h00000000;
			reg_wdata <= 32'h00000000;
		end else begin
			pc_oper <= `PC_NORMAL;
			pc_jmp <= 32'h00000000;
			pc_br <= 32'h00000000;
			alu_op <= `PC_NORMAL;
			alu_num1 <= 32'h00000000;
			alu_num2 <= 32'h00000000;
			ram_wen <= 1'b0;
			ram_addr <= 32'h00000000;
			ram_wdata <= 32'h00000000;
			reg_wen <= 1'b0;
			reg_waddr <= 32'h00000000;
			reg_wdata <= 32'h00000000;
			
			case (inst_op)
				`INST_OP_LW: begin  // rt = *(rs+imme)
					reg_wen <= 1'b1;
					reg_waddr <= inst_num2;
					reg_wdata <= ram_rdata;
					reg_raddr1 <= inst_num1;

					alu_num1 <= reg_rdata1;
					alu_num2 <= {16'h0000,inst_imme};
					alu_op <= `ALU_ADD;

					ram_addr <= alu_res;
				end
				`INST_OP_SW: begin      // *(rs+imme) = rt;
					reg_raddr1 <= inst_num1;	// rs
					reg_raddr2 <= inst_num2;	// rt

					alu_num1 <= reg_rdata1;		// rs
					alu_num2 <= {16'h0000,inst_imme};
					alu_op <= `ALU_ADD;
					
					ram_wen <= 1'b1;
					ram_addr <= alu_res;
					ram_wdata <= reg_rdata2;   // rt
				end
				`INST_OP_BEQ: begin
					reg_raddr1 <= inst_num1;
					reg_raddr2 <= inst_num2;
					
					alu_num1 <= reg_rdata1;
					alu_num2 <= reg_rdata2;
					alu_op <= `ALU_SUB;

					if(alu_res == 0) begin
						pc_oper <= `PC_BR;
						pc_br <= {16'h0000,inst_imme};	
					end
				end
				`INST_OP_LUI: begin
					reg_wen <= 1'b1;
					reg_waddr <= inst_num2;
					reg_wdata <= {inst_imme,16'h0000};
				end
				`INST_OP_ADDIU: begin
					reg_wen <= 1'b1;
					reg_waddr <= inst_num2;
					reg_wdata <= alu_res;
					reg_raddr1 <= inst_num1;

					alu_num1 <= reg_rdata1;
					alu_num2 <= {16'h0000,inst_imme};
					alu_op <= `ALU_ADD;
				end
				`INST_OP_J: begin
					pc_oper <= `PC_JMP;
					pc_jmp <= {16'h0000,inst_imme};
				end
				`INST_OP_FUNC: begin
					case (inst_func) 
						`INST_FUNC_ADD:begin
							reg_wen <= 1'b1;
							reg_waddr <= inst_num3;
							reg_wdata <= alu_res;
							reg_raddr1 <= inst_num1;
							reg_raddr2 <= inst_num2;

							alu_num1 <= reg_rdata1;
							alu_num2 <= reg_rdata2;
							alu_op <= `ALU_ADD;
						end
						`INST_FUNC_SUB:begin
							reg_wen <= 1'b1;
							reg_waddr <= inst_num3;
							reg_wdata <= alu_res;
							reg_raddr1 <= inst_num1;
							reg_raddr2 <= inst_num2;

							alu_num1 <= reg_rdata1;
							alu_num2 <= reg_rdata2;
							alu_op <= `ALU_SUB;
						end
						`INST_FUNC_SLL: begin
							reg_wen <= 1'b1;
							reg_waddr <= inst_num2;
							reg_wdata <= alu_res;
							reg_raddr1 <= inst_num3;

							alu_num1 <= reg_rdata1;
							alu_num2 <= {27'h0,inst_shamt};
							alu_op <= `ALU_SLL;
						end
					endcase
				end
			endcase
		end
	end

endmodule

